---
type: integration
title: '@astrojs/mdx'
description: 了解如何在你的 Astro 项目中使用 @astrojs/mdx 集成。
sidebar:
  label: MDX
githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/mdx/'
category: other
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'
import ReadMore from '~/components/ReadMore.astro'
import Since from '~/components/Since.astro'

此 **[Astro 集成][astro-integration]** 能够使用 [MDX](https://mdxjs.com/) 组件，并允许你通过 `.mdx` 文件创建页面。

## 为什么是 MDX？

MDX 允许你在 Astro 项目的 Markdown 内容中使用变量、JSX 表达式和组件。如果你有现有的用 MDX 编写的内容，这个集成允许你把这些文件带到你的 Astro 项目中。

## 安装

Astro 包含了一个 `astro add` 命令，用于自动设置官方集成。如果你愿意，可以改为[手动安装集成](#手动安装)。

在新的终端窗口中运行下面其中一个命令。

<PackageManagerTabs>
  <Fragment slot="npm">
  ```sh
  npx astro add mdx
    ```
  </Fragment>
  <Fragment slot="pnpm">
  ```sh
  pnpm astro add mdx
  ```
  </Fragment>
  <Fragment slot="yarn">
  ```sh
  yarn astro add mdx
  ```
  </Fragment>
 </PackageManagerTabs>

如果你遇到任何问题，[请随时在 GitHub 上向我们报告](https://github.com/withastro/astro/issues)并尝试下面的手动安装步骤。

### 手动安装

首先，安装 `@astrojs/mdx` 包： 

<PackageManagerTabs>
  <Fragment slot="npm">
  ```sh
  npm install @astrojs/mdx
  ```
  </Fragment>
  <Fragment slot="pnpm">
  ```sh
  pnpm add @astrojs/mdx
  ```
  </Fragment>
  <Fragment slot="yarn">
  ```sh
  yarn add @astrojs/mdx
  ```
  </Fragment>
</PackageManagerTabs>

然后，使用 `integrations` 属性将集成应用到你的 `astro.config.*` 文件中：

```js title="astro.config.mjs" ins={2} ins="mdx()"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
  // ...
  integrations: [mdx()],
});
```

### 编辑器集成

对于 [VS Code](https://code.visualstudio.com/) 的编辑器支持，请安装[官方的 MDX 扩展](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx)。

对于其他编辑器，请使用 [MDX 语言服务器](https://github.com/mdx-js/mdx-analyzer/tree/main/packages/language-server)。

## 使用

访问 [MDX 文档](https://mdxjs.com/docs/what-is-mdx/)，了解如何使用标准 MDX 功能。

## Astro 中的 MDX

添加 MDX 集成可以使用 JSX 变量、表达式和组件来增强你的 Markdown 编写体验。

它还为标准 MDX 添加了额外的功能，包括对 MDX 中 Markdown 风格的 frontmatter 的支持。这允许你使用大部分 [Astro 内置的 Markdown 功能](/zh-cn/guides/markdown-content/)。

`.mdx` 文件必须使用 [MDX 语法](https://mdxjs.com/docs/what-is-mdx/#mdx-syntax) 而不能使用像 Astro 那样类 HTML 的语法编写。

### 在 MDX 中使用内容集合

要将 MDX 文件包含在内容集合中，请确保你的 [集合 loader](/zh-cn/guides/content-collections/#定义集合-loader) 配置为从 `.mdx` 文件加载内容：

```js title="src/content.config.ts" ins="mdx"
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/blog" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
  })
});

export const collections = { blog };
```

### 在 MDX 中使用导出的变量

MDX 支持使用 `export` 语句将变量添加到 MDX 内容，或者将数据导出给需要引入它的组件。

例如，你可以从 MDX 页面或组件导出 `title` 字段以用作 `{JSX 表达式}` 的标题：

```mdx title="/src/blog/posts/post-1.mdx"
export const title = '我的第一篇 MDX 博客'

# {title}
```

或者，你可以使用 `import` 和 `import.meta.glob()` 语句在页面中使用导出的 `title`：

```astro title="src/pages/index.astro"
---
const matches = import.meta.glob('./posts/*.mdx', { eager: true });
const posts = Object.values(matches);
---

{posts.map(post => <p>{post.title}</p>)}
```

#### 导出的属性

当使用 `import` 语句或 `import.meta.glob()` 时，`.astro` 组件可以使用以下属性：

- **`file`** - 文件的绝对路径（例如：`/home/user/projects/.../file.mdx`）。
- **`url`** - 页面的 URL（例如：`/zh-cn/guides/markdown-content`）。
- **`frontmatter`** - 包含了文件的 YAML/TOML frontmatter 中所指定的任何数据。
- **`getHeadings()`** - 一个异步函数，用于返回文件中所有标题（`<h1>` 到 `<h6>`）的数组，类型为：`{ depth: number; slug: string; text: string }[]`。每个标题的 `slug` 都对应了给定标题生成的 ID，可用于锚点链接。
- **`<Content />`** - 返回文件完整渲染内容的组件。
- **（任意的 `export` 值）** - MDX 文件还可以使用 `export` 语句以导出数据。

### 在 MDX 中使用 Frontmatter 变量

Astro MDX 集成默认支持在 MDX 中使用 frontmatter。在 Markdown 文件中添加 frontmatter 属性，这些变量可在模板中以及在导入文件时以命名属性的形式访问。

```mdx title="/src/blog/posts/post-1.mdx"
---
title: '我的第一篇 MDX 博客'
author: 'Houston'
---

# {frontmatter.title}

作者：{frontmatter.author}
```

### 在 MDX 中使用组件

安装 MDX 集成后，你可以在 MDX (`.mdx`) 文件中导入并使用 [Astro 组件](/zh-cn/basics/astro-components/)和 [UI 框架组件](/zh-cn/guides/framework-components/#使用框架组件)。就像在任何其他 Astro 组件中使用它们一样。

别忘了在你的 UI 框架组件上添加 `client:directive`，如果需要的话！

在 [MDX 文档](https://mdxjs.com/docs/what-is-mdx/#esm)中查看有关使用导入和导出语句的更多示例。

```mdx title="src/blog/post-1.mdx" {4,10}
---
title: 我的第一篇博客
---
import ReactCounter from '../components/ReactCounter.jsx';

我刚开始折腾一个新的 Astro 博客！

这是我的计数器组件，它在 MDX 中工作：

<ReactCounter client:load />
```
#### 将自定义组件分配给 HTML 元素

使用 MDX，你可以将 Markdown 语法映射到自定义组件，而不是它们的标准 HTML 元素。这允许你以标准的 Markdown 语法编写，但是将特殊的组件样式应用于所选的元素。

例如，你可以创建一个 `Blockquote.astro` 组件来为 `<blockquote>` 内容提供自定义样式：

```astro title="src/components/Blockquote.astro"
---
const props = Astro.props;
---
<blockquote {...props} class="bg-blue-50 p-4">
  <span class="text-4xl text-blue-600 mb-2">“</span>
  <slot /> <!-- 请务必为子组件添加 `<slot/>`！ -->
</blockquote>
```

将你的自定义组件导入 `.mdx`文件，然后将标准 HTML 元素映射到自定义组件的 `components` 对象以导出：

```mdx title="src/blog/posts/post-1.mdx"
import Blockquote from '../components/Blockquote.astro';
export const components = {blockquote: Blockquote}

> 这个引用将是自定义引用块组件。
```
访问 [MDX 网站](https://mdxjs.com/table-of-components/)，了解可以覆盖为自定义组件的 HTML 元素的完整列表。

:::note
在 MDX 文件中定义并导出的自定义组件必须始终先被导入，然后通过 `components` 属性传回给 `<Content />` 组件。
:::

#### 将 `components` 传给 MDX 内容

在使用 `<Content />` 组件渲染导入的 MDX 内容时（包括使用内容集合渲染 MDX 条目），可以通过 `components` 属性传入自定义组件。这些组件必须先被导入，才能使 `<Content />` 组件可用。

`components` 对象将 HTML 元素名称（`h1`、`h2`、`blockquote` 等）映射到你的自定义组件。你也可以使用扩展运算符（...）[包含从 MDX 文件本身导出的所有组件](#将自定义组件分配给-html-元素)，这些组件也必须作为 `components` 从你的 MDX 文件中导入。

如果你直接从单个文件导入 MDX 在 Astro 组件中使用，请同时导入 `Content` 组件以及你 MDX 文件中导出的任何组件。

```astro title="src/pages/page.astro" "components={{...components, h1: Heading }}" {2}
---
import { Content, components } from '../content.mdx';
import Heading from '../Heading.astro';
---
<!-- 为 # 语法创建自定义<h1>， _并且_ 在`content.mdx`中应用任何自定义组件 -->
<Content components={{...components, h1: Heading }} />
```

如果你的 MDX 文件是内容集合中的一条条目，则使用来自 `astro:content` 的 `render()` 函数来访问 `<Content />` 组件。

下面的示例通过 `components` 属性向 `<Content />` 组件传入一个自定义标题，以取代所有 `<h1>` HTML 元素：


```astro title="src/pages/blog/post-1.astro" ins="components={{ h1: CustomHeading }}"
---
import { getEntry, render } from 'astro:content';
import CustomHeading from '../../components/CustomHeading.astro';
const entry = await getEntry('blog', 'post-1');
const { Content } = await render(entry);
---
<Content components={{ h1: CustomHeading }} />
```

## 配置

一旦 MDX 集成被安装，在你的 Astro 项目中使用 `.mdx` 文件就不需要配置。

你可以通过以下选项配置你的 MDX 的渲染方式：

* [继承自 Markdown 配置的选项](#继承自-markdown-配置的选项)
* [`extendMarkdownConfig`](#extendmarkdownconfig)
* [`recmaPlugins`](#recmaplugins)
* [`optimize`](#optimize)

### 继承自 Markdown 配置的选项

所有 [`markdown` 配置选项](/zh-cn/reference/configuration-reference/#markdown-选项)都可以在 MDX 集成中单独配置。这包括 remark 和 rehype 插件、语法高亮等。选项将默认为你的 Markdown 配置中的选项（[请参阅 `extendMarkdownConfig` 选项](#extendmarkdownconfig)来修改此选项）。

```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import remarkToc from 'remark-toc';
import rehypePresetMinify from 'rehype-preset-minify';

export default defineConfig({
  // ...
  integrations: [
    mdx({
      syntaxHighlight: 'shiki',
      shikiConfig: { theme: 'dracula' },
      remarkPlugins: [remarkToc],
      rehypePlugins: [rehypePresetMinify],
      remarkRehype: { footnoteLabel: 'Footnotes' },
      gfm: false,
    }),
  ],
});
```

:::caution
MDX 不支持以字符串形式传递备注和 rehype 插件。你应该安装、导入并应用插件功能来代替。
:::

<ReadMore>参见 [Markdown 选项参考](/zh-cn/reference/configuration-reference/#markdown-选项) 以获得完整的选项列表。</ReadMore>

### `extendMarkdownConfig`

<p>

**类型：** `boolean`<br />
**默认值：** `true`<br />
<Since v="0.15.0" pkg="@astrojs/mdx" />
</p>

MDX 将默认扩展[你的项目现有的 Markdown 配置](/zh-cn/reference/configuration-reference/#markdown-选项)。要覆盖个别选项，你可以在你的 MDX 配置中进行等价配置。

例如，假设你需要禁用 GitHub-Flavored Markdown，并为 MDX 文件应用一套不同的注释插件。你可以像这样应用这些选项，`extendMarkdownConfig` 默认为启用：

```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

export default defineConfig({
  // ...
  markdown: {
    syntaxHighlight: 'prism',
    remarkPlugins: [remarkPlugin1],
    gfm: true,
  },
  integrations: [
    mdx({
      // 继承自 Markdown 的 `syntaxHighlight`

      // Markdown `remarkPlugins` 被忽略，
      // 只启用 `remarkPlugin2`。
      remarkPlugins: [remarkPlugin2],
      // `gfm` 被重写为 `false`
      gfm: false,
    }),
  ],
});
```

你可能还需要在 MDX 中禁用 `markdown` 配置扩展。为此，将 `extendMarkdownConfig` 设置为 `false`：

```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

export default defineConfig({
  // ...
  markdown: {
    remarkPlugins: [remarkPlugin1],
  },
  integrations: [
    mdx({
      // Markdown 配置现在被忽略了
      extendMarkdownConfig: false,
      // `remarkPlugins` 没有启用
    }),
  ],
});
```

### `recmaPlugins`


<p>

**类型：** `PluggableList`<br />
**默认值：** `[]`<br />
<Since v="0.11.5" pkg="@astrojs/mdx" />
</p>

这些是直接修改输出 [estree](https://github.com/estree/estree) 的插件。这对于在你的 MDX 文件中修改或注入 JavaScript 变量很有用。

我们建议[使用 AST Explorer](https://astexplorer.net/)来处理 estree 的输出，并尝试 [`estree-util-visit`](https://unifiedjs.com/explore/package/estree-util-visit/) 来搜索整个 JavaScript 节点。

### optimize

<p>

**类型：** `boolean | { ignoreElementNames?: string[] }`<br />
**默认值：** `false`<br />
<Since v="0.19.5" pkg="@astrojs/mdx" />
</p>

这是一个可选的配置设置，用于优化 MDX 输出，以便通过内部 rehype 插件加快构建和渲染速度。如果你的 MDX 文件较多，并注意到构建速度较慢，这可能会很有用。不过，该选项可能会生成一些未转义的 HTML，因此请确保你网站的交互式部分在启用该选项后仍能正常工作。

默认情况下是禁用的。要启用 MDX 优化，请在 MDX 集成配置中添加以下内容：

```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

export default defineConfig({
  // ...
  integrations: [
    mdx({
      optimize: true,
    }),
  ],
});
```

#### ignoreElementNames

<p>

**类型：** `string[]`<br />
<Since v="3.0.0" pkg="@astrojs/mdx" />
</p>

<p><Since pkg="@astrojs/mdx" v="3.0.0" /></p>
以前称为 `customComponentNames`。

`optimize` 的一个可选属性，用于防止 MDX 优化器处理某些元素名称，例如通过 components 属性传递给导入的 MDX 内容的[自定义组件](/zh-cn/guides/integrations-guide/mdx/#将-components-传给-mdx-内容)。

你需要将这些组件从优化中排除，因为优化器会急切地将内容转换为静态字符串，这将破坏需要动态呈现的自定义组件。

例如，以下内容的预期 MDX 输出应为 `<Heading>...</Heading>`，而不是每个 `"<h1>...</h1>"`：

```astro
---
import { Content, components } from '../content.mdx';
import Heading from '../Heading.astro';
---

<Content components={{ ...components, h1: Heading }} />
```

要使用 `ignoreElementNames` 属性配置此优化，指定应视为自定义组件的 HTML 元素名称数组：

```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

export default defineConfig({
  // ...
  integrations: [
    mdx({
      optimize: {
        // 防止优化器处理 `h1` 元素
        ignoreElementNames: ['h1'],
      },
    }),
  ],
});
```

请注意，如果你的 MDX 文件[使用 `export const components = {...}` 配置自定义组件](/zh-cn/guides/integrations-guide/mdx/#将自定义组件分配给-html-元素)，则你无需手动配置此选项。优化器将自动检测它们。


## 例子

* [Astro MDX 启动模板](https://github.com/withastro/astro/tree/latest/examples/with-mdx)显示了如何在你的 Astro 项目中使用 MDX 文件。

[astro-integration]: /zh-cn/guides/integrations-guide/

[astro-ui-frameworks]: /zh-cn/guides/framework-components/#使用框架组件
